﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Linq;
using System.Text.RegularExpressions;
using System.Transactions;

namespace VersionStar.App
{
    public class SqlScriptExecutor
    {
        private readonly OleDbConnection _connection;
        private readonly OleDbTransaction _transaction;

        public SqlScriptExecutor(OleDbConnection connection, OleDbTransaction transaction)
        {
            _connection = connection;
            _transaction = transaction;
            if (_connection.State != ConnectionState.Open)
            {
                _connection.Open();
            }
        }

        public void Execute(string script)
        {
            IEnumerable<string> commands = GetCommandsFromScript(script);

            foreach (string command in commands)
            {
                if (!string.IsNullOrWhiteSpace(command))
                {
                    ExecuteCommand(command);
                }
            }
        }

        private static IEnumerable<string> GetCommandsFromScript(string script)
        {
            Regex regex = new Regex(@"\r{0,1}\nGO\s*\r{0,1}\n", RegexOptions.IgnoreCase);
            List<string> commands =
                regex
                    .Split(script + "\n")
                    .Select(s => s.Trim())
                    .Where(s => !string.IsNullOrWhiteSpace(s))
                    .ToList();

            return commands;
        }

        private void ExecuteCommand(string sql)
        {
            try
            {
                const int commandTimeoutInMinutes = 15;
                using (OleDbCommand command = new OleDbCommand(sql, _connection))
                {
                    if (_transaction != null)
                    {
                        command.Transaction = _transaction;
                    }
                    command.CommandTimeout = 60 * commandTimeoutInMinutes;
                    command.ExecuteNonQuery();
                    command.Dispose();
                }
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("The script failed to execute: \n" + PartialString(sql, 120) + "\n", e);
            }
        }

        private string PartialString(string str, int maxLength)
        {
            if (str.Length <= maxLength)
            {
                return str;
            }
            else
            {
                return str.Substring(0, maxLength);
            }
        } 
    }    
}